using System;

namespace MT5Trade.Models
{
    /// <summary>
    /// 确定性伪随机数生成器
    /// 基于线性同余生成器（LCG）实现，保证结果可重现
    /// 符合规则要求：禁止使用Random类，必须使用确定性算法
    /// </summary>
    public class DeterministicRandomProvider : IRandomProvider
    {
        #region 常量定义

        // 使用Knuth推荐的LCG参数
        private const ulong A = 1664525;      // 乘数 (Knuth推荐)
        private const ulong C = 1013904223;   // 增量 (Knuth推荐)
        private const ulong M = 4294967296;   // 模数 2^32

        #endregion

        #region 私有字段

        private ulong _seed;
        private bool _hasSpare;
        private double _spare;

        #endregion

        #region 构造函数

        /// <summary>
        /// 使用指定种子初始化确定性随机数生成器
        /// </summary>
        /// <param name="seed">随机数种子</param>
        public DeterministicRandomProvider(ulong seed)
        {
            _seed = seed;
            _hasSpare = false;
        }

        /// <summary>
        /// 使用基于时间和上下文的种子初始化
        /// </summary>
        /// <param name="contextId">上下文标识符（如账户ID）</param>
        public DeterministicRandomProvider(string contextId = null)
        {
            // 基于日期和上下文生成确定性种子
            var dateHash = (ulong)DateTime.Today.Ticks;
            var contextHash = string.IsNullOrEmpty(contextId) 
                ? 0UL 
                : (ulong)contextId.GetHashCode();
            
            _seed = dateHash ^ contextHash;
            _hasSpare = false;
        }

        #endregion

        #region IRandomProvider 实现

        /// <summary>
        /// 生成0到1之间的伪随机双精度浮点数
        /// 使用线性同余算法：X(n+1) = (A * X(n) + C) mod M
        /// </summary>
        /// <returns>大于等于0且小于1的双精度数</returns>
        public double NextDouble()
        {
            // 线性同余生成器公式
            _seed = (A * _seed + C) % M;
            
            // 将种子值归一化到[0,1)区间
            return (double)_seed / M;
        }

        /// <summary>
        /// 生成指定范围内的伪随机整数
        /// </summary>
        /// <param name="minValue">包含的下界</param>
        /// <param name="maxValue">不包含的上界</param>
        /// <returns>大于等于minValue且小于maxValue的整数</returns>
        public int Next(int minValue, int maxValue)
        {
            if (minValue >= maxValue)
            {
                throw new ArgumentOutOfRangeException(
                    nameof(maxValue), 
                    "maxValue必须大于minValue");
            }

            double random = NextDouble();
            int range = maxValue - minValue;
            return (int)(random * range) + minValue;
        }

        /// <summary>
        /// 生成不超过指定值的非负伪随机整数
        /// </summary>
        /// <param name="maxValue">不包含的上界</param>
        /// <returns>大于等于0且小于maxValue的整数</returns>
        public int Next(int maxValue)
        {
            if (maxValue <= 0)
            {
                throw new ArgumentOutOfRangeException(
                    nameof(maxValue), 
                    "maxValue必须大于0");
            }

            return Next(0, maxValue);
        }

        /// <summary>
        /// 生成正态分布的伪随机数
        /// 使用Box-Muller变换将均匀分布转换为正态分布
        /// </summary>
        /// <param name="mean">均值（默认0）</param>
        /// <param name="stdDev">标准差（默认1）</param>
        /// <returns>服从正态分布的随机数</returns>
        public double NextGaussian(double mean = 0, double stdDev = 1)
        {
            // Box-Muller变换实现
            // 每次调用生成两个独立的正态分布随机数，交替返回
            
            if (_hasSpare)
            {
                _hasSpare = false;
                return _spare * stdDev + mean;
            }

            double u1, u2;
            do
            {
                u1 = NextDouble();
                u2 = NextDouble();
            } while (u1 <= 0); // 避免log(0)

            // Box-Muller变换
            double mag = stdDev * Math.Sqrt(-2.0 * Math.Log(u1));
            _spare = mag * Math.Cos(2.0 * Math.PI * u2);
            _hasSpare = true;

            return mag * Math.Sin(2.0 * Math.PI * u2) + mean;
        }

        #endregion

        #region 静态工厂方法

        /// <summary>
        /// 创建用于优化算法的确定性随机数生成器
        /// 种子基于账户ID和当前日期，确保每日结果可重现
        /// </summary>
        /// <param name="accountId">账户标识符</param>
        /// <returns>配置好的随机数提供器</returns>
        public static DeterministicRandomProvider CreateForOptimization(string accountId)
        {
            return new DeterministicRandomProvider(accountId);
        }

        /// <summary>
        /// 创建用于测试的确定性随机数生成器
        /// 使用固定种子确保测试结果完全可重现
        /// </summary>
        /// <param name="testSeed">测试种子</param>
        /// <returns>配置好的随机数提供器</returns>
        public static DeterministicRandomProvider CreateForTesting(ulong testSeed = 42)
        {
            return new DeterministicRandomProvider(testSeed);
        }

        #endregion

        #region 辅助方法

        /// <summary>
        /// 重置种子值
        /// </summary>
        /// <param name="seed">新的种子值</param>
        public void Reset(ulong seed)
        {
            _seed = seed;
            _hasSpare = false;
        }

        /// <summary>
        /// 获取当前种子值（用于调试和测试）
        /// </summary>
        /// <returns>当前种子值</returns>
        public ulong GetCurrentSeed()
        {
            return _seed;
        }

        #endregion
    }
}